#ifndef NIGHT_ARRAY_SHAPE
#define NIGHT_ARRAY_SHAPE

#include "core.hpp"

namespace night {

namespace array {

template< int i, typename Size > struct at;
template< typename Type, typename Shape > struct join;
template< typename Shape > struct empty;
template< typename Shape > struct head;
template< typename Shape > struct tail;

template< size_type iSize, size_type jSize, size_type kSize > 
struct size 
{ 
	enum { i = iSize, j = jSize, k = kSize };
	static const size_type dim = ( iSize ? 1 : 0 ) + ( jSize ? 1 : 0 ) + ( kSize ? 1 : 0 );

	template< int i > struct base : at<i,size> { };

	static size_type get ( int n ) { return n==0 ? iSize : n==1 ? jSize : n==2 ? kSize : 0; }
	static size_type length () { return ( iSize ? iSize : 0 ) * ( jSize ? jSize : 1 ) * ( kSize ? kSize : 1 ); }
};


template< size_type src >
struct at_size {
	static const size_type value = src; 
	typedef size<value> type;
	template< typename Size > static type apply ( Size ) { return type (); }
};

template< size_type iSize, size_type jSize, size_type kSize >
struct at < 0, size<iSize,jSize,kSize> > : at_size<iSize> {};
template< size_type iSize, size_type jSize, size_type kSize >
struct at < 1, size<iSize,jSize,kSize> > : at_size<jSize> {};
template< size_type iSize, size_type jSize, size_type kSize >
struct at < 2, size<iSize,jSize,kSize> > : at_size<kSize> {};


template< size_type iSize, size_type jSize, size_type kSize >
struct join< size<>, size<iSize,jSize,kSize> > { 
	typedef size<iSize,jSize,kSize> type; 
	template< typename S1, typename S2 > static type apply ( S1, S2 ) { return type (); }
};

template< size_type iSize, size_type jSize, size_type kSize, size_type qSize >
struct join< size<iSize>, size<jSize,kSize,qSize> > { 
	typedef size<iSize,jSize,kSize> type; 
	template< typename S1, typename S2 > static type apply ( S1, S2 ) { return type (); }
};

template< size_type iSize, size_type jSize, size_type kSize, size_type qSize, size_type rSize >
struct join< size<iSize,jSize>, size<kSize,qSize,rSize> > { 
	typedef size<iSize,jSize,kSize> type; 
	template< typename S1, typename S2 > static type apply ( S1, S2 ) { return type (); }
};

template< size_type iSize, size_type jSize, size_type kSize, size_type qSize, size_type rSize, size_type sSize >
struct join< size<iSize,jSize,kSize>, size<qSize,rSize,sSize> > { 
	typedef size<iSize,jSize,kSize> type; 
	template< typename S1, typename S2 > static type apply ( S1, S2 ) { return type (); }
};


template< size_type iSize, size_type jSize, size_type kSize >
struct empty < size<iSize,jSize,kSize> > { typedef size<> type; };


template< size_type iSize, size_type jSize, size_type kSize >
struct head< size<iSize,jSize,kSize> > {
	typedef size<iSize> type;
	template< typename S1 > static type apply ( S1 ) { return type (); }
};


template< size_type iSize, size_type jSize, size_type kSize >
struct tail< size<iSize,jSize,kSize> > {
	typedef size<jSize,kSize> type;
	template< typename S1 > static type apply ( S1 ) { return type (); }
};

} // end namespace night::array

} // end namespace night

#endif
